Skip to content

fix(jcs): enforce fail-closed RFC 8785 canonicalization - #253

Draft
Deleted user (ghost) wants to merge 7 commits into
mainfrom
fix/rfc8785-fail-closed-canonicalization
Draft

fix(jcs): enforce fail-closed RFC 8785 canonicalization#253
Deleted user (ghost) wants to merge 7 commits into
mainfrom
fix/rfc8785-fail-closed-canonicalization

Conversation

@ghost

@ghost ghost commented Aug 1, 2026

Copy link
Copy Markdown

Purpose

Close a concrete canonicalization soundness gap on current main@0bdffe75b56e5cd27c0632e1ba166620da327494.

The existing serializer was labeled RFC 8785 but diverged at security-critical boundaries:

  • property ordering used Unicode code points rather than unsigned UTF-16 code units;
  • lone surrogate code units were escaped instead of rejected;
  • BigInt was silently converted into a quoted decimal string;
  • undefined object properties disappeared;
  • sparse arrays, custom class instances, accessors, symbol keys and cycles were not uniformly rejected.

These behaviors can produce a commitment for data that is not valid ordinary JSON/JCS input, or produce a commitment that differs from a compliant implementation.

Changes

  • enforce UTF-16 code-unit property ordering;
  • reject invalid Unicode, including lone surrogates;
  • reject BigInt, undefined, functions and symbols rather than changing/omitting their type;
  • accept only plain JSON objects and dense arrays;
  • reject accessors without invoking getters;
  • reject cycles, sparse arrays and non-index array properties;
  • add the normative RFC 8785 Section 3.2.3 ordering vector;
  • replace implicit BigInt-coercion tests with explicit decimal-string boundary tests;
  • add adversarial negative cases for each fail-closed boundary.

Evidence produced in this session

Targeted TypeScript transpilation: PASS.

Targeted runtime checks against the branch implementation: PASS for:

  • all embedded conformance vectors;
  • normative UTF-16 ordering vector;
  • BigInt rejection;
  • nested undefined rejection;
  • lone-surrogate rejection;
  • sparse-array rejection;
  • accessor rejection without getter invocation;
  • cycle rejection.

A full repository clone/test run could not be executed in the current sandbox because outbound DNS resolution for github.com was unavailable. Exact-head GitHub Actions remain mandatory before admission.

Generated provenance boundary

.claude.json was changed by the repository's branch-provenance automation (source_ref / parent_state_hash). It was not manually edited as part of the JCS implementation. Treat it as a generated artifact and verify it through its canonical generator/gate.

Authority boundary

  • Draft only.
  • No merge authorization.
  • No deployment, database migration, credential, IAM, DNS, billing or external publication change.
  • action_required, skipped, absent or non-executed checks are not evidence of a pass.
  • Admission requires exact-head CI plus review of any compatibility impact from rejecting non-JSON values that were previously coerced or omitted.

Reconciliation relation

This PR is a narrow implementation repair against the canonicalization boundary and should be recorded under issue #246 as:

  • VERIFIES: RFC 8785 canonical byte semantics;
  • SUPERSEDES: implicit BigInt-to-string canonicalization behavior;
  • CONFLICTS_WITH: callers that pass non-JSON runtime values directly into the integrity boundary.

@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hook-generator Ready Ready Preview Aug 1, 2026 12:37am
hub Ready Ready Preview Aug 1, 2026 12:37am
platform-picker Ready Ready Preview Aug 1, 2026 12:37am

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
aegisomega f768914 Commit Preview URL

Branch Preview URL
Aug 01 2026, 12:37 AM

@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/tariks-projects-e9198507?upgradeToPro=build-rate-limit

ghost commented Aug 1, 2026

Copy link
Copy Markdown
Author

Exact-head admission status and compatibility boundary

Current snapshot (2026-08-01):

  • current main: 0bdffe75b56e5cd27c0632e1ba166620da327494
  • PR head: f7689145ffd24ac7c6a796ef5e621b1b674c14f5
  • topology: 7 commits ahead, 0 behind; merge base equals current main
  • Experiment Admission, Integration Ledger, Sovereignty Contracts, Scale OS Controls, Kernel One, Automaton-2, Automaton-3, OSV, Hadolint and the Constitutional Automaton all conclude success on this exact head.
  • Gate 1 JCS anchor, the full TypeScript suite, typecheck, build, Python bridge, Rust suites, observability and six field builds passed.
  • The separate informational TS Coverage Thresholds job failed inside the otherwise successful Constitutional run. It is not authority-bearing under the current ceremony, but remains unresolved tooling evidence and must not be described as green coverage.

Compatibility audit findings so far:

  • the primary EventStore path already projects sequence to a decimal string before canonicalization and reconstructs BigInt only outside the integrity boundary;
  • sovereignty and Scale OS sequence/fencing fields are typed as canonical decimal strings;
  • open-ended boundaries such as hashValue(value: unknown), computeMerkleRootFromValues(values: unknown[]), canonicalizeScaleOSSourceObjectV1(sourceObject: unknown) and canonicalizeScaleOSPayloadV1(payload: unknown) will now reject non-JSON runtime values rather than silently coercing or omitting them. This is the intended security behavior, but every caller must explicitly project such values into ordinary JSON data.

Remaining admission conditions:

  1. complete the runtime caller inventory for all direct and indirect JCS/hash entry points;
  2. add compatibility tests proving supported callers project BigInt, undefined, class instances and sparse data before the integrity boundary;
  3. retain negative tests proving unsupported values fail closed;
  4. regenerate .claude.json through its canonical provenance generator if the final head changes;
  5. either repair the V8 coverage collector or explicitly preserve its non-authoritative status without claiming coverage success.

Status: CONDITIONALLY ADMISSIBLE SECURITY REPAIR; KEEP DRAFT UNTIL CALLER COMPATIBILITY IS CLOSED. No merge authorization.

ghost commented Aug 1, 2026

Copy link
Copy Markdown
Author

Admission status correction — caller compatibility is now a hard blocker

The fail-closed JCS implementation itself remains functionally supported by the exact-head gate set already recorded for f7689145ffd24ac7c6a796ef5e621b1b674c14f5. However, a direct caller audit has identified integrity call paths that currently pass SequenceNumber values (bigint) into hashValue, which #253 correctly rejects.

Confirmed incompatible callers on the current PR head:

  • src/frame/dfa.ts: transition and certificate payloads hash SequenceNumber directly.
  • src/frame/lineage.ts: computeLineageHash() hashes SequenceNumber directly.
  • src/frame/topology.ts: topologyPayload() includes SequenceNumber directly.
  • src/ledger/verify.ts: verifyChain() hashes the complete LedgerEntry, whose sequence field is SequenceNumber.

These are not JCS defects. They are pre-existing caller-contract violations exposed by the corrected integrity boundary. Their required classification is EXPLICIT_DOMAIN_ENCODING, using canonical unsigned decimal strings before hashing. Automatic generic BigInt coercion must not be restored.

Current surface classifications:

  • hashValue(value: unknown): EXPECTED_FAIL_CLOSED; callers must supply ordinary JSON or explicit domain encodings.
  • computeMerkleRootFromValues(values: readonly unknown[]): EXPECTED_FAIL_CLOSED; known string/ordinary-JSON projections are safe, non-JSON values must fail.
  • Scale OS sourceObject: unknown: EXPECTED_FAIL_CLOSED at the external integrity boundary.
  • Scale OS payload: unknown: EXPECTED_FAIL_CLOSED at the external integrity boundary.

Required closure before admission:

  1. Replace every integrity-bound SequenceNumber with an explicit canonical decimal-string projection.
  2. Add compatibility tests proving SAFE_JSON_PROJECTION, EXPLICIT_DOMAIN_ENCODING, or EXPECTED_FAIL_CLOSED for each audited surface.
  3. Repeat the caller search for all hashValue / computeMerkleRootFromValues call sites and close every non-JSON path.
  4. Rerun exact-head CI and regenerate the admission receipt for the new candidate SHA.

Therefore the current classification is:

BLOCKED ON CALLER COMPATIBILITY — KEEP DRAFT — NO MERGE AUTHORITY

The existing exact-head evidence remains valid only for f7689145ffd24ac7c6a796ef5e621b1b674c14f5; any compatibility patch creates a new candidate requiring new evidence.

ghost commented Aug 2, 2026

Copy link
Copy Markdown
Author

Downstream dependency recorded: PR #258 imports canonicalizeJCS from the core module repaired here. Therefore #258 must not be admitted against the pre-#253 base. Required order: admit #253 on exact-head evidence after caller-compatibility review; rebase/replay #258 onto post-#253 main; regenerate or confirm its LifeQuest golden vector; then execute exact-head tests/typecheck. Until that sequence completes, #258 remains draft with no merge authority.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants